package service

import (
	"ant-agent/exec/model"
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"gitee.com/openeuler/PilotGo/sdk/logger"
)

var JobsBaseDir = "./srv/jobs"
var ScriptsBaseDir = "./srv/scripts"

// 根据脚本类型获取文件后缀
func getScriptSuffix(scriptType string) (suffix string, err error) {
	switch scriptType {
	case "Shell":
		return ".sh", nil
	case "Python":
		return ".py", nil
	case "Perl":
		return ".pl", nil
	case "Ruby":
		return ".rb", nil
	case "PHP":
		return ".php", nil
	default:
		return "", fmt.Errorf("不支持的脚本类型: %s", scriptType)
	}
}

// 根据脚本全名（含后缀）获取解释器
func getScriptInterpreter(scriptPath string) (interpreter string, err error) {
	switch filepath.Ext(scriptPath) {
	case ".sh":
		return "/bin/ksh", nil
	case ".py":
		return "/usr/bin/python3", nil
	case ".pl":
		return "/usr/bin/perl", nil
	case ".rb":
		return "/usr/bin/ruby", nil
	case ".php":
		return "/usr/bin/php", nil
	default:
		return "", fmt.Errorf("不支持的脚本后缀: %s", filepath.Ext(scriptPath))
	}
}

// 简单的转义字符解析函数
func parseEscapeSequences(s string) string {
	s = strings.ReplaceAll(s, "\r", "")
	return s
}

func createScriptFile(tag, scriptId, suffix, encodedScript string) (string, error) {
	decodedScript, err := base64.StdEncoding.DecodeString(encodedScript)
	if err != nil {
		return "", fmt.Errorf("脚本内容base64解码失败: %s", err.Error())
	}
	parsedScript := parseEscapeSequences(string(decodedScript)) // 解析转义字符

	scriptDir := fmt.Sprintf("%s/%s", ScriptsBaseDir, tag)
	if _, err := os.Stat(scriptDir); os.IsNotExist(err) {
		if err := os.MkdirAll(scriptDir, 0755); err != nil {
			return "", fmt.Errorf("创建脚本存储目录失败: %s", err.Error())
		}
	}
	scriptPath := fmt.Sprintf("%s/%s%s", scriptDir, scriptId, suffix)

	newContentHash := calculateSHA256([]byte(parsedScript))
	if isFileUnchanged(scriptPath, newContentHash) {
		logger.Debug("脚本文件不需要更新: %s", scriptPath)
		return scriptPath, nil
	}

	if err := os.WriteFile(scriptPath, []byte(parsedScript), 0755); err != nil {
		return "", fmt.Errorf("写入脚本文件失败: %s", err.Error())
	}

	if err := os.Chmod(scriptPath, 0755); err != nil {
		os.Remove(scriptPath)
		return "", fmt.Errorf("设置脚本可执行权限失败: %s", err.Error())
	}

	logger.Debug("创建脚本文件成功: %s", scriptPath)
	return scriptPath, nil
}

func getScriptFilePath(tag, scriptId string) (string, error) {
	tagDir := filepath.Join(ScriptsBaseDir, tag)

	if _, err := os.Stat(tagDir); os.IsNotExist(err) {
		return "", fmt.Errorf("tag目录不存在: %s", tagDir)
	}

	var foundPath string
	var foundCount int

	// 只在tag目录下查找
	err := filepath.Walk(tagDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		if !info.IsDir() && strings.HasPrefix(info.Name(), scriptId) {
			foundPath = path
			foundCount++
			if foundCount > 1 {
				return fmt.Errorf("在tag目录 %s 下找到多个以 %s 开头的脚本文件", tag, scriptId)
			}
		}
		return nil
	})

	if err != nil {
		return "", err
	}

	if foundCount == 1 {
		return foundPath, nil
	}

	return "", fmt.Errorf("在tag目录 %s 下未找到以 %s 开头的脚本文件", tag, scriptId)
}

func createScriptResultPath(jobId, scriptId string) (string, error) {
	resultDir := fmt.Sprintf("%s/jobId_%s", JobsBaseDir, jobId)
	if _, err := os.Stat(resultDir); os.IsNotExist(err) {
		if err := os.MkdirAll(resultDir, 0755); err != nil {
			return "", fmt.Errorf("创建执行结果存储目录失败: %s", err.Error())
		}
	}

	resultFilePath := fmt.Sprintf("%s/result_%s.json", resultDir, scriptId)
	if _, err := os.Stat(resultFilePath); os.IsNotExist(err) {
		if err := os.WriteFile(resultFilePath, []byte("{}"), 0644); err != nil {
			return "", fmt.Errorf("创建空的 result_%s.json 文件失败: %s", scriptId, err.Error())
		}
	}
	return resultFilePath, nil
}

// 创建job存储路径
func createJobResultPath(jobId string) (string, error) {
	resultDir := fmt.Sprintf("%s/jobId_%s", JobsBaseDir, jobId)
	if _, err := os.Stat(resultDir); os.IsNotExist(err) {
		if err := os.MkdirAll(resultDir, 0755); err != nil {
			return "", fmt.Errorf("创建执行结果存储目录失败: %s", err.Error())
		}
	}

	resultFilePath := fmt.Sprintf("%s/result.json", resultDir)
	if _, err := os.Stat(resultFilePath); os.IsNotExist(err) {
		if err := os.WriteFile(resultFilePath, []byte("{}"), 0644); err != nil {
			return "", fmt.Errorf("创建空的 result.json 文件失败: %s", err.Error())
		}
	}
	return resultFilePath, nil
}

func writeResultToFile(filePath string, result any) error {
	data, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		logger.Error("序列化结果失败: %s", err.Error())
		return err
	}

	if err := os.WriteFile(filePath, data, 0644); err != nil {
		logger.Error("写入结果文件失败: %s", err.Error())
		return err
	}

	return nil
}

func runScriptWithCancel(interpreter, scriptPath string, params string, timeoutSec int, cancelChan chan bool) (string, string, string, int, error) {
	if timeoutSec <= 0 {
		timeoutSec = 30
	}

	fullCmd := fmt.Sprintf("table=%s %s", params, scriptPath)
	cmd := exec.Command(interpreter, "-c", fullCmd)

	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return "", fmt.Sprintf("启动脚本执行失败: %s", err.Error()), model.SubJobFailed, -1, fmt.Errorf("启动脚本执行失败: %s", err.Error())
	}

	done := make(chan error, 1)
	go func() {
		done <- cmd.Wait()
	}()

	select {
	case <-time.After(time.Duration(timeoutSec) * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			return stdout.String(), stderr.String(), model.SubJobFailed, 1, fmt.Errorf("脚本执行超时, 但终止进程失败: %s", err.Error())
		}
		return stdout.String(), stderr.String(), model.SubJobTimeout, 1, fmt.Errorf("脚本执行超时, 已终止进程")
	case <-cancelChan:
		if err := cmd.Process.Kill(); err != nil {
			return stdout.String(), stderr.String(), model.SubJobFailed, 1, fmt.Errorf("任务已取消, 但终止进程失败: %s", err.Error())
		}
		return stdout.String(), stderr.String(), model.SubJobCanceled, 1, fmt.Errorf("任务已取消, 已终止进程")
	case err := <-done:
		stdoutStr := strings.TrimSpace(stdout.String())
		stderrStr := strings.TrimSpace(stderr.String())

		var exitCode int
		if err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				exitCode = exitErr.ExitCode()
			} else {
				exitCode = 1
			}
		} else {
			exitCode = cmd.ProcessState.ExitCode()
		}

		if exitCode == 0 {
			return stdoutStr, stderrStr, model.SubJobSuccess, exitCode, nil
		} else {
			var returnErr error
			if err != nil {
				returnErr = err
			} else {
				returnErr = fmt.Errorf("脚本执行失败，退出状态码: %d", exitCode)
			}
			return stdoutStr, stderrStr, model.SubJobFailed, exitCode, returnErr
		}
	}
}

// 检查文件是否存在且内容未变化
func isFileUnchanged(filePath string, newContentHash string) bool {
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return false
	}

	existingContent, err := os.ReadFile(filePath)
	if err != nil {
		return false
	}

	existingHash := calculateSHA256(existingContent)

	return existingHash == newContentHash
}

// 计算SHA256
func calculateSHA256(content []byte) string {
	hash := sha256.Sum256(content)
	return hex.EncodeToString(hash[:])
}
